#!usr/bin/env python3
# -*- encoding: utf-8 -*-

import re
from string import Template
from typing import Any
import json
import allure

from jsonpath import jsonpath
from loguru import logger
from pathlib import Path

# 获取当前文件所在目录的上一级目录的绝对路径
"""
使用Path(__file__)获取当前文件的路径。
使用.resolve(strict=True)将相对路径转换为绝对路径，并确保路径存在。
使用.parent.parent获取上一级目录的路径。 
这个函数通常用于在配置文件、资源文件等需要指定路径的情况下，动态获取路径，以方便文件的读写操作。
"""
BASE_DIR = Path(__file__).resolve(strict=True).parent.parent

print("BASE_DIR:", BASE_DIR)


def exec_func(func: str) -> str:
    """
    执行函数。
    参数:
    - func: 字符串类型的函数名。
    返回值:
    - 执行结果的字符串类型。
    - 运行时根据输入的字符串动态调用已存在的函数并获取其执行结果
    这种场景常见于需要动态执行代码或脚本的情况，但需要注意的是，
    由于使用了exec函数，可能会带来安全隐患，因为它允许执行任意Python代码。
    在实际应用中应谨慎使用，确保输入的安全性。
    例如，如果你有一个名为add_two_numbers的函数，
    你可以在运行时通过传入字符串 "add_two_numbers" 到 exec_func 函数来执行它并获取结果，
    而无需在代码中直接硬编码函数调用。
    """
    loc = locals()  # 获取当前作用域的局部变量
    exec(f"result = {func}")  # 动态执行函数
    return str(loc['result'])  # 返回执行结果


def extractor(obj: dict, expr: str = '.') -> Any:
    """
    从字典中提取特定表达式对应的数据。
    参数:
    - obj: 待提取数据的字典对象。
    - expr: 表达式，用于指定要提取的数据路径，默认为'.'表示提取全部。
    返回值:
    - 符合表达式指定的数据。
    """
    try:
        if not jsonpath(obj, expr):
            # 当表达式不适合json格式时，采用另一种方法提取
            split_expr = expr.rsplit('.', maxsplit=1)  # 分割表达式
            expr_sec = split_expr[-2]
            expr_first = split_expr[-1]
            result_sec = jsonpath(obj, expr_sec)[0]  # 提取第二部分结果
            result = json.loads(result_sec)[expr_first]  # 解析并提取最终结果
        else:
            result = jsonpath(obj, expr)[0]  # 直接根据表达式提取结果
    except Exception as e:
        logger.error(f'{expr} - 无法从字典中提取数据: {e}')
        result = expr  # 提取失败时返回表达式本身
    return result


def rep_expr(content: str, data: dict) -> str:
    """
    替换字符串中的表达式为实际值。
    参数:
    - content: 原始字符串内容。
    - data: 包含要替换的键值对的字典。
    返回值:
    - 替换后的字符串。
    """
    logger.info(f'content-{content}')
    content = Template(content).safe_substitute(data)  # 安全替换模板中的变量
    for func in re.findall('\\${(.*?)}', content):  # 查找并执行所有`${...}`形式的函数
        try:
            logger.info(f"exec_func(func)-{exec_func(func)}")
            content = content.replace("${%s}" % func, exec_func(func))  # 替换函数执行结果
        except Exception as e:
            logger.error(f'rep_expr-{e}')
    return content


def convert_json(dict_str: str) -> dict:
    """
    将类似字典的字符串转换为字典类型。
    参数:
    - dict_str: 字典样的字符串。
    返回值:
    - 转换后的字典对象。
    """
    try:
        if 'None' in dict_str:
            dict_str = dict_str.replace('None', 'null')
        elif 'True' in dict_str:
            dict_str = dict_str.replace('True', 'true')
        elif 'False' in dict_str:
            dict_str = dict_str.replace('False', 'false')
        dict_str = json.loads(dict_str)  # 尝试使用json.loads转换
    except Exception as e:
        if 'null' in dict_str:
            dict_str = dict_str.replace('null', 'None')
        elif 'true' in dict_str:
            dict_str = dict_str.replace('true', 'True')
        elif 'false' in dict_str:
            dict_str = dict_str.replace('false', 'False')
        dict_str = eval(dict_str)  # 使用eval作为备用转换方法
        logger.error(f'dict_str-{dict_str}')
        logger.error(e)
    return dict_str


def allure_title(title: str) -> None:
    """
    设置Allure报告的标题。
    参数:
    - title: 报告的标题字符串。
    """
    allure.dynamic.title(title)
    logger.info(f'title:{title}')


def allure_step(step: str, var: str) -> None:
    """
    执行测试步骤，并为步骤添加附件。
    参数:
    - step: 测试步骤的描述。
    - var: 要作为附件添加到测试步骤的数据。
    """
    with allure.step(step):
        allure.attach(
            json.dumps(
                var,
                ensure_ascii=False,
                indent=4),  # 将变量以美观的JSON格式附加到步骤中
            step,
            allure.attachment_type.JSON)
    logger.info(f'step:{step}')


def allure_step_no(step: str):
    """
    执行测试步骤，不添加附件。
    参数:
    - step: 测试步骤的描述。
    """
    with allure.step(step):
        logger.info(f'step:{step}')
